#pragma once

class D3dTexture;

namespace gradients {

class BaseGradient
{
public:
	static void Interpolate(COLORF &out, const COLORF *colors, uint colorCount, float t);

	virtual ~BaseGradient() { }
	virtual void GetColorAt(COLORF &out, float t) = 0;
	// Has default implementation.
	virtual void RenderAll(COLORF *outColors, uint count);
};

// Gradient from pixel colors of middle row of given texture, loaded with Direct3D.
class TextureGradient : public BaseGradient
{
public:
	void Load(const string &filePath);
	virtual void GetColorAt(COLORF &out, float t);

private:
	std::vector<COLORF> m_Colors;
};

// Gradient from selected color map from inside cmap.UGR file from Apophysis application.
class ApophysisGradient : public BaseGradient
{
public:
	ApophysisGradient();
	void Load(const string &filePath, const string &gradientName);
	virtual void GetColorAt(COLORF &out, float t);

private:
	COLORF m_Colors[400];

	static bool CharInIdentifier(char ch);
	static void SkipWhitespace(CharReader &cr);
	static void ExpectSymbol(CharReader &cr, char ch);
	static bool QuerySymbol(CharReader &cr, char ch);
	static void ExpectIdentifier(CharReader &cr, const string &s);
	static void SkipIdentifier(CharReader &cr);
	static void SkipString(CharReader &cr);
	static void SkipValue(CharReader &cr);
	static void ParseIdentifier(string &outName, CharReader &cr);
	static void SkipGradientContent(CharReader &cr);
	void ParseGradientContent(CharReader &cr);
};

class GimpGradient : public BaseGradient
{
public:
	GimpGradient();

	void Load(const string &filePath);

	const COLORF & GetBackgroundColor() const { return m_BackgroundColor; }
	const COLORF & GetForegroundColor() const { return m_ForegroundColor; }
	void SetBackgroundColor(const COLORF &color) { m_BackgroundColor = color; }
	void SetForegroundColor(const COLORF &color) { m_ForegroundColor = color; }

	virtual void GetColorAt(COLORF &out, float t);

private:
	enum GimpGradientColor
	{
		GIMP_GRADIENT_COLOR_FIXED,
		GIMP_GRADIENT_COLOR_FOREGROUND,
		GIMP_GRADIENT_COLOR_FOREGROUND_TRANSPARENT,
		GIMP_GRADIENT_COLOR_BACKGROUND,
		GIMP_GRADIENT_COLOR_BACKGROUND_TRANSPARENT
	};

	enum GimpGradientSegmentType
	{
		GIMP_GRADIENT_SEGMENT_LINEAR,
		GIMP_GRADIENT_SEGMENT_CURVED,
		GIMP_GRADIENT_SEGMENT_SINE,
		GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING,
		GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING
	};

	enum GimpGradientSegmentColor
	{
		GIMP_GRADIENT_SEGMENT_RGB,      /* normal RGB           */
		GIMP_GRADIENT_SEGMENT_HSV_CCW,  /* counterclockwise hue */
		GIMP_GRADIENT_SEGMENT_HSV_CW    /* clockwise hue        */
	};

	struct GimpGradientSegment
	{
		float left, middle, right;
		GimpGradientColor left_color_type, right_color_type;
		COLORF left_color, right_color;
		GimpGradientSegmentType type;
		GimpGradientSegmentColor color;

		static int KeyCmp(const float &pos, const GimpGradientSegment &seg) { return common::UniversalCmp(pos, seg.left); }
	};

	struct GimpHSV { float h, s, v, a; };

	std::vector<GimpGradientSegment> m_Segments;
	COLORF m_BackgroundColor, m_ForegroundColor;

	const GimpGradientSegment * GetSegmentAtInterval(float t) const;

	static float CalcLinearFactor(float middle, float pos);
	static float CalcCurvedFactor(float middle, float pos);
	static float CalcSineFactor(float middle, float pos);
	static float CalcSphereIncreasingFactor(float middle, float pos);
	static float CalcSphereDecreasingFactor(float middle, float pos);
	static void GimpRgbToHsv(GimpHSV &hsv, const COLORF &rgb);
	static void GimpHsvToRgb(COLORF &rgb, const GimpHSV &hsv);
};

} // namespace gradients
